/* ========================================================================
 * itf_psa.c
 * 
 * Copyright (C) 2011 Andr� Pedro - apedro@gmx.com
 * 
 * The frontend of learning GSMP algorithm for Matlab. Using Qt to display
 * windows and diagrams, and Graphviz to arrange graphically the nodes,
 * the transitions and the labels before showing in Qt environment.
 * 
 * As output produces a learned model from sample executions in
 * two formats, first on a language interpreted by modelchecker Ymer,
 * and second, stores a set of matrices in the Matlab environment.
 *
 * ======================================================================*/
/* FILE: $Id: itf_ptasa.cpp 20 2011-09-30 20:05:09Z apedro $ */

#include <stdio.h>
/*#include <windows.h>*/
/*#include <process.h>*/
#include <time.h>

#include <iostream>
#include <ostream>
#include <sstream>

/* Qt headers */
#include <QApplication>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsEllipseItem>
#include <QStyleOptionGraphicsItem>
#include <QPrinter>
#include <QList>
#include <QTextEdit>

/* Diagram arrangement algorithm headers */
#include <graphviz/gvc.h>

/* Main app - gui_sdesframework */
#include "ui/gui_sdesframework.h"

/* Display graphs */
#include "itf_graph_xy.h"

/* Interface headers */
#include "itf_debug.h"
#include "itf_garrangement.h"
#include "itf_mc.h"

/* Data structure functions headers */
#include "prefix_tree.h"
#include "path.h"
#include "pt_ext.h"

/*
 * Algorithm headers of inverse scheduller estimation (ISE),
 * probability similarity of states (PSS), and model selection (MS).
 */
#include "alg_ise.h"
#include "alg_pss.h"
#include "alg_ms.h"

/* The Matlab interface header */
#include "mex.h"

#define mxIsScalar(a) (!mxIsDouble(a) || mxIsComplex(a) || (mxGetN(a)*mxGetM(a)!=1))

using namespace std;

extern void _main();

/*static HANDLE hThread=NULL;*/

void gui_thread_func(void *pArguments) {
    QApplication *app;
    QMainWindow *mw;
    Ui_MainWindow *ui_mw;
    GraphicArrangement *graphic;
    Plot *plot;
    
    int argc = 0;
    char **argv = NULL;
    
    psa *trie = (psa *)pArguments;
    
    // Initialize Qt environment
    app = new QApplication(argc, argv);

    mw = new QMainWindow();

    ui_mw = new Ui_MainWindow();

    ui_mw->setupUi(mw);
    
    // Create graphs
    plot = new Plot(ui_mw->tab);
    ui_mw->verticalLayout_2->addWidget(plot->getQwtPlot());

    // Create a Graphic visualizer object
    graphic = new GraphicArrangement();
    
    DEBUG_PRINT((FDEBUG, "SHOW Trie\n"));

    graphic->fromTrie(trie);

    //mexPrintf("Init layout process ...\n");
    graphic->setLayout("dot");

    mexPrintf("Bounding box ...\n");
    QGraphicsScene scene(graphic->boundingRect());

    mexPrintf("Display trie\n");
    graphic->display(&scene);

    ui_mw->graphicsView->setScene(&scene);
    ui_mw->graphicsView->setRenderHints(
            QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
    
    ui_mw->textEdit->setPlainText(QString::fromStdString(pt_ext::lang(trie)));
    
    
    // Connect state to graph plot
    QMapIterator<int, Event*> it_ev(graphic->getEventItems());
    
    while (it_ev.hasNext()) {
        it_ev.next();
        
        QObject::connect((it_ev.value()), SIGNAL(displayECDF(double *,unsigned int,unsigned int,unsigned int)), plot, SLOT(setValue(double *,unsigned int,unsigned int,unsigned int)));
    }

    // show main window
    mw->show();
    
    app->exec();
    
    mexPrintf("Delete graphic object\n");
    delete graphic;
    
    return 0;
}


/* The Matlab interface funtion, called 'itf_ptasa(P,'type')'. */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {

    unsigned int i, nogui=0;
    int argn;
    
    struct samples sample_runs;
    psa *trie;
    
    struct parameters *param;
    char tmp[100];
    
    double el_time;
    clock_t t1, t2;
    
    unsigned threadID;
    
    std:stringstream oss;
    std::cout.rdbuf(oss.rdbuf());
    
    DEBUG_INIT("debug_itfptasa.log");
    DEBUG_PRINT((FDEBUG, "DEBUG initialization of GSMP learning process.\n"));
    
     /*if(mexIsLocked()) {
        WaitForSingleObject( hThread, INFINITE );
        CloseHandle( hThread );
        mexUnlock();
        mexEvalString("clear itf_ptasa");
        return;
    }*/
    
    // Input data from Matlab matrices
    /* ================================================================= */
    // Get sample executions
    itf_path(&sample_runs, nrhs, prhs);
    
    // Input parameters
    param = create_parameters();
    if((nrhs-1) % 2 == 0 && nrhs > 0) {
        for (i=1; i<(unsigned int)nrhs; i+=2) {
            //mexPrintf("One\n");
            if (mxIsChar(prhs[i]) && !mxIsScalar(prhs[i+1])) {
                mxGetString(prhs[i], tmp, 100);
                setparameters(param, tmp, mxGetScalar(prhs[i+1]));
            }
            else
                mexErrMsgTxt("The type of arguments do not match.");
        }
    }
    else
        mexErrMsgTxt("The arguments do not match.");
    
    
    /* ================================================================= */
    
    // Create the prefix tree
    trie = pt_create();
    
    /*if (param->gui) {
        mexLock();
        
        // Create the second thread.
        mexPrintf( "Creating gui_thread_func...\n" );
        
        hThread = (HANDLE)_beginthreadex( NULL, 0, &gui_thread_func, (void *) (trie) , 0, &threadID );
    }*/
    
    // start time measure
    t1 = clock();
    
    // Insert the sample executions into prefix tree
    /* ================================================================= */
    for(i=0; i<sample_runs.n; i++) {
        /* DEBUG */
        //mexPrintf("Paths Debug ...\n");
        pt_insert(trie, sample_runs.arrayPaths[i]);
        
#ifdef DEBUG
        path_debug(sample_runs.arrayPaths[i],
                   sample_runs.arrayPaths[i]->firstNode);
#endif
    }
    /* ================================================================= */
    
    // Apply ISE algorithm to estimate the old clock values
    ise_algorithm(trie, sample_runs.arrayPaths, sample_runs.n);
    
    /* DEBUG clocks ISE */
#ifdef DEBUG
    DEBUG_PRINT((FDEBUG, "BEGIN - CLOCKS FROM ISE...\n"));
    for(i=0; i<sample_runs.n; i++) {
        path_debug(sample_runs.arrayPaths[i],
                   sample_runs.arrayPaths[i]->firstNode);
    }
    DEBUG_PRINT((FDEBUG, "END - CLOCKS FROM ISE\n"));
#endif
    
    // Convert prefix tree as stochastic automaton
    normalize_inward(trie);
    // --------------------------------------------------------------------
    
    // Probabilistic equality of states
    if (param->method == 1)
        pss(trie);
    // --------------------------------------------------------------------
    
    
    // Estimate the parameters
    // --------------------------------------------------------------------
    model::selection(trie);
    // --------------------------------------------------------------------
    
    
    t2 = clock();
    el_time = ((float)t2 - (float)t1)/CLOCKS_PER_SEC;
    mexPrintf("*******************************************************\n");
    mexPrintf("Elapsed time: %f %d\n", ((float)t2 - (float)t1), CLOCKS_PER_SEC);
    mexPrintf("*******************************************************\n");
    
    
    // Output the structure of learned model
    pt_ext::matlab_gsmp_structure(trie, el_time, plhs, &nlhs);
    
    
    mexPrintf("=======================================================\n");
    mexPrintf("Model\n");
    mexPrintf("=======================================================\n");
    mexPrintf("%s", pt_ext::lang(trie));
    mexPrintf("=======================================================\n");
    
    
    if (param->gui) {
        gui_thread_func((void *)trie);
    }
    
    // redirect to matlab
    mexPrintf("--------------------------------------------------------\n");
    //mexPrintf("STDOUT.>\n%s\n", oss.str());
    
    /* Remove trie */
    
    // Free parameters structure
    free_parameters(param);
    
    // Free sample runs
    free_itf_path(&sample_runs);
    
    // remove ntrie
    pt_free_prefix_tree(trie);
    
    // Close the debug file
    DEBUG_END();
    
    return;
}
